﻿using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;

namespace Helpers
{
    public static class ConsulExtensions
    {
        /// <summary>
        /// 添加consul,注册IConsulClient,并注册自身服务内网ip和端口,docker部署需要添加参数--net host,iis部署需要配置端口号
        /// </summary>
        public static IServiceCollection AddConsulz(this IServiceCollection services, IConfiguration configuration, Action<ConsulOptions> configure = null)
        {
            services.AddOptions<ConsulOptions>()
                .Bind(configuration.GetSection(ConsulOptions.SectionName))
                .ValidateDataAnnotations();
            services.PostConfigure<ConsulOptions>(x =>
            {
                configure?.Invoke(x);
            });
            services.AddSingleton<ConsulOptions>(x => x.GetRequiredService<IOptions<ConsulOptions>>().Value);

            services.AddSingleton<IStartupFilter, ConsulStartupFilter>();
            services.AddSingleton<IConsulClient>(p => new ConsulClient(x => x.Address = new Uri(p.GetRequiredService<ConsulOptions>().url)));
            return services;
        }

        /// <summary>
        /// 向consul注册服务
        /// </summary>
        internal static async Task UseConsulz(this IApplicationBuilder app)
        {
            var consulOptions = app.ApplicationServices.GetRequiredService<ConsulOptions>();
            if (string.IsNullOrEmpty(consulOptions?.serviceName)) return;

            var lifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>();
            var features = app.Properties["server.Features"] as FeatureCollection;
            var consulClient = app.ApplicationServices.GetRequiredService<IConsulClient>();

            var port = consulOptions.servicePort ?? new Uri(features.Get<IServerAddressesFeature>()
                .Addresses
                .FirstOrDefault().Replace("*", "0.0.0.0")).Port;
            Console.WriteLine($"consul服务端口号--{port}");

            NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces(); //适配器
            var firstIpV4Address = consulOptions.serviceIp ?? adapters
                .Where(x => x.Name == "eth0" || x.Name.Contains("以太网"))
                .Select(p => p.GetIPProperties())
                .SelectMany(p => p.UnicastAddresses)
                .Where(p => p.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork && !System.Net.IPAddress.IsLoopback(p.Address))
                .Select(x => x.Address.ToString())
                .FirstOrDefault();
            Console.WriteLine($"consul本地ip--{firstIpV4Address}");

            var httpCheck = new AgentServiceCheck()
            {
                DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),
                Interval = TimeSpan.FromSeconds(30),
                HTTP = new Uri(new Uri($"http://{firstIpV4Address}:{port}"), consulOptions.httpCheckPath).ToString(),
                Timeout = TimeSpan.FromSeconds(5)
            };

            var registration = new AgentServiceRegistration()
            {
                Checks = new[] { httpCheck },
                ID = $"{consulOptions.serviceName}_{firstIpV4Address}:{port}",
                Name = consulOptions.serviceName,
                Address = firstIpV4Address,
                Port = port,
            };
            await consulClient.Agent.ServiceRegister(registration);

            lifetime.ApplicationStopping.Register(() =>
            {
                consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(false).GetAwaiter().GetResult();
            });
        }


    }
}
